import axios from "axios"
import qs from "qs"
import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from "axios"
import { Promise } from "es6-promise"
import {
  RequestConfig,
  RequestInterceptors,
  CancelRequestSource,
  IUrlUniKey,
} from "./types"

class Request {
  instance: AxiosInstance
  interceptorsOj?: RequestInterceptors
  cancelRequestSourceList?: CancelRequestSource[]
  requestUrlList?: string[]

  constructor(config: RequestConfig) {
    this.instance = axios.create(config)
    const c = config || {}
    this.interceptorsOj = c.interceptors || {}
    this.requestUrlList = []
    this.cancelRequestSourceList = []

    this.instance.interceptors.request.use(
      this.interceptorsOj.requestInterceptors,
      this.interceptorsOj.requestInterceptorsCatch
    )
    this.instance.interceptors.response.use(
      this.interceptorsOj.responseInterceptors,
      this.interceptorsOj.responseInterceptorsCatch
    )

    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        return res
      },
      (err: any) => {
        if (err.name !== "CanceledError") {
          return err.response
        }
        return Promise.reject(err)
      }
    )
  }
  private delUrl(url: string) {
    if (
      this.requestUrlList &&
      typeof this.requestUrlList.filter === "function"
    ) {
      this.requestUrlList = this.requestUrlList.filter((u) => u !== url)
    }
    if (
      this.cancelRequestSourceList &&
      typeof this.cancelRequestSourceList.filter === "function"
    ) {
      this.cancelRequestSourceList = this.cancelRequestSourceList.filter(
        (item) => {
          return Object.keys(item)[0] !== url
        }
      )
    }
  }
  private urlUniqKey(config: IUrlUniKey) {
    const keyList: string[] = []
    keyList.push(config.url)
    if (config.method != null) {
      keyList.push(config.method.toUpperCase())
    }
    if (config.params != null) {
      keyList.push(qs.stringify(config.params))
    }
    if (config.data != null) {
      keyList.push(qs.stringify(config.data))
    }
    return keyList.join()
  }
  private filterUrls(args: IUrlUniKey): CancelRequestSource[] {
    const key = this.urlUniqKey(args)
    if (
      this.cancelRequestSourceList &&
      typeof this.cancelRequestSourceList.filter === "function"
    ) {
      return this.cancelRequestSourceList.filter((item) => {
        return Object.keys(item)[0].indexOf(key) === 0
      }) as []
    }
    return []
  }
  private excelCancelRequests(list: CancelRequestSource[]) {
    list.forEach((item) => {
      const key = Object.keys(item)[0]
      if (key == null) return
      if (item[key] == null) return
      item[key]()
      this.delUrl(key)
    })
  }
  private removeRepeatRequest(key: string) {
    if (
      this.cancelRequestSourceList &&
      typeof this.cancelRequestSourceList.filter === "function"
    ) {
      const rList = this.cancelRequestSourceList.filter((item) => {
        return Object.keys(item)[0] === key
      })
      if (rList && rList.length) {
        this.excelCancelRequests(rList)
      }
    }
  }
  request<T>(config: RequestConfig): Promise<T> {
    return new Promise((resolve) => {
      if (
        config &&
        config.interceptors &&
        config.interceptors.requestInterceptors
      ) {
        config = config.interceptors.requestInterceptors(
          config as InternalAxiosRequestConfig
        )
      }

      const hasClearSameRequest = config.hasNotClearSameRequest
        ? !config.hasNotClearSameRequest
        : true

      const urlKey = this.urlUniqKey(config as IUrlUniKey)
      if (urlKey) {
        if (hasClearSameRequest) {
          this.removeRepeatRequest(urlKey)
        }
        if (
          this.requestUrlList &&
          typeof this.requestUrlList.push === "function"
        ) {
          this.requestUrlList.push(urlKey)
        }
        config.cancelToken = new axios.CancelToken((c) => {
          if (
            this.cancelRequestSourceList &&
            typeof this.cancelRequestSourceList.push === "function"
          ) {
            this.cancelRequestSourceList.push({
              [urlKey]: c,
            })
          }
        })
      }

      this.instance
        .request<any, AxiosResponse<T>>(config)
        .then((res) => {
          if (
            config &&
            config.interceptors &&
            config.interceptors.responseInterceptors
          ) {
            res =
              config.interceptors.responseInterceptors<AxiosResponse<T>>(res)
          }
          if (!!config.containHeader) {
            resolve(<any>res)
          } else {
            resolve(res.data)
          }
          urlKey && this.delUrl(urlKey)
        })
        .catch((err: any) => {
          //
        })
    })
  }
  cancelAllRequest() {
    if (
      this.cancelRequestSourceList &&
      typeof this.cancelRequestSourceList.forEach === "function"
    )
      this.cancelRequestSourceList.forEach((source) => {
        const key = Object.keys(source)[0]
        source[key]()
      })
  }
  cancelRequest(args: IUrlUniKey | IUrlUniKey[]) {
    if (Array.isArray(args)) {
      args.forEach((u) => {
        if (u && u.url) {
          const cancelList = this.filterUrls(u)
          this.excelCancelRequests(cancelList)
        }
      })
    } else {
      if (!args || !args.url) return
      const cancelList = this.filterUrls(args)
      this.excelCancelRequests(cancelList)
    }
  }
}

export default Request
